package aquarium.reseau;

import java.awt.Point;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

import aquarium.gui.*;
import aquarium.items.*;

public class ClientTCP extends Thread {

	private double TIMEOUT = 5000;
	private double _last_packet_time = 0;

	private int _port;
	private String _address;
	private Socket _socket;

	private InputStreamReader _depuisServeur = null;
	private PrintStream _versServeur = null;
	private BufferedReader _buffer;

	private Aquarium _aquarium;
	private Animation _animation;
	private HashMap<Integer, ArrayList<AquariumItem>> _list_packet_aquarium_items;
	private ArrayList<AquariumItem> _list_tmp = new ArrayList<AquariumItem>();

	public void start(String address ,int port){
		this._port = port;
		this._address = address;

		this.start();
	}
	
	public void run(){

		try
		{		

			aquarium_init();

			//_port = 9999;
			try {
				_socket = new Socket(_address, _port);
			} catch (Exception e){
				System.out.println("Erreur : Serveur d'écoute inexistant sur le port " + _port + " et machine " + _address + ".");
				_animation.close();
			}
			// ouvre le flux
			_depuisServeur = new InputStreamReader(_socket.getInputStream());
			_versServeur = new PrintStream(_socket.getOutputStream());

			_buffer = new BufferedReader(_depuisServeur); // regarde la réponse du serveur
			String message;

			ArrayList<String> tableau = creatPackageTCP();

			double time;
			this._last_packet_time = System.currentTimeMillis();

			while(true){

				time = System.currentTimeMillis();

				if(time - _last_packet_time > TIMEOUT){
					close();
				}

				while(_buffer.ready()){
					message = _buffer.readLine();
					decrypteReponse(message); // Mettre la fonction de traitement de donnée par l'aquarium
					_last_packet_time = time;

				}

				tableau = creatPackageTCP(); // Envoi de sa collection lorsqu'elle a été modifié
				for(int i = 0 ; i < tableau.size(); i++){
					_versServeur.println(tableau.get(i));
				}

				try {
					ClientTCP.sleep((long) 1);
				} catch (Exception e) {
					System.out.println("Exception while trying to sleep. Too mich noise !!! for details see this -> " + e);
				}
			}

		}
		catch (UnknownHostException e)
		{
			e.printStackTrace();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	/****   Fonctions  ****/

	private void aquarium_init(){
		_aquarium = new Aquarium(); // On lance l'aquarium seul
		_animation = new Animation(_aquarium);

		_aquarium.randomFishGenerator(_aquarium.getCollection());

		Windows _windows = new Windows();
		_windows.start(_animation);

		this._list_packet_aquarium_items = _aquarium.get_hashmap_list();
	}

	public ArrayList<String> creatPackageTCP(){
		if (!_aquarium.modified) {
			return new ArrayList<String>();
		}
		Collection<AquariumItem> items = _aquarium.getCollection();
		synchronized(items){
			ArrayList<String> elements = new ArrayList<String>();
			String description = null;
			int i = 0;
			Iterator<AquariumItem> it = items.iterator();
			while (it.hasNext()) 
			{
				AquariumItem ai = it.next();
				if (ai instanceof MobileItem){
					if(!((MobileItem)ai).is_visitor()){
						if(ai instanceof NormalFish)
							description = new String("n ");
						if(ai instanceof OneSecondFish)
							description = new String("o ");
						if(ai instanceof PiscivoreFish)
							description = new String("p ");
						if(ai instanceof Bubble)
							description = new String("b ");


						description = description.concat(String.valueOf(ai.get_position().x)).concat(" ");
						description = description.concat(String.valueOf(ai.get_position().y)).concat(" ");
						description = description.concat(String.valueOf(ai.get_size()).concat(" "));
						description = description.concat(String.valueOf(ai.get_adult_size()).concat(" "));
						description = description.concat(String.valueOf(((MobileItem) ai).get_target().x)).concat(" ");
						description = description.concat(String.valueOf(((MobileItem) ai).get_target().y).concat(" "));
						if(ai instanceof Fish && ((Fish)ai).get_sex() == Sex.Female)
							description = description.concat("f");
						else
							description = description.concat("m");

						elements.add(new String(description));
						i++;
					}
				}
			}
			elements.add(new String("end"));
			_aquarium.modified = false;
			return elements;
		}
	}

	public void decrypteReponse(String message){
		String[] tab = new String[12];
		tab = message.split(" "); // On met dans un tableau chaques éléments
		
		if ((message.lastIndexOf("end") == -1)){
			char type = tab[0].charAt(0); // Transformation en char du String du type d'item
			int positionx = Integer.parseInt(tab[1]);
			int positiony = Integer.parseInt(tab[2]);
			int size = Integer.parseInt(tab[3]);
			int adult_size = Integer.parseInt(tab[4]);
			int targetx = Integer.parseInt(tab[5]);
			int targety = Integer.parseInt(tab[6]);
			char sc = tab[7].charAt(0);
			Sex sex;
			if (sc == 'f')
				sex = Sex.Female;
			else
				sex = Sex.Male;

			Point position = new Point(positionx, positiony);
			Point target = new Point(targetx, targety);

			if (_aquarium.getGraphic() == null){
				_aquarium.getdraw();
			}

			Fish f = null;
			switch(type){
			case 'm' :
				Stone st = new Stone(position, adult_size);
				st.set_size(adult_size);
				_aquarium.getCollection().add(st);
				break;
			case 's' :
				Seaweed sw = new Seaweed(position, adult_size);
				sw.set_size(adult_size);
				_aquarium.getCollection().add(sw);
				break;
			case 'b' :
				Bubble b = new Bubble(position, size, false, true, 100, -1, target, true, null);
				_list_tmp.add(b);
				break;
			case 'n' :
				f = new NormalFish(position, adult_size, -1, -1, target, sex, true, null);
				f.set_size(size);
				_list_tmp.add(f);
				break;
			case 'o' :
				f = new OneSecondFish(position, adult_size, -1, -1, target, sex, true, null);
				f.set_size(size);
				_list_tmp.add(f);
				break;
			case 'p' :
				f = new PiscivoreFish(position, adult_size, -1, -1, target, sex, true, null);
				f.set_size(size);
				_list_tmp.add(f);
				break;
			default :
				break;
			}
		}
		else{
			if (tab.length != 1){
				int id = Integer.parseInt(tab[1]);
				synchronized(_list_packet_aquarium_items){
					_list_packet_aquarium_items.put(id, new ArrayList<AquariumItem>(_list_tmp));
					_list_tmp.clear();
				}
			}
			else
				_list_tmp.clear();
		}
	}

	private void close(){
		try {
			_socket.close();
			_animation.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

